home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
pcl
/
src-16f.lha
/
code
/
package.lisp
< prev
next >
Wrap
Lisp/Scheme
|
1992-05-30
|
50KB
|
1,427 lines
;;; -*- Log: code.log; Package: Lisp -*-
;;;
;;; **********************************************************************
;;; This code was written as part of the CMU Common Lisp project at
;;; Carnegie Mellon University, and has been placed in the public domain.
;;; If you want to use this code or any part of CMU Common Lisp, please contact
;;; Scott Fahlman or slisp-group@cs.cmu.edu.
;;;
(ext:file-comment
"$Header: package.lisp,v 1.17 92/03/13 23:27:54 ram Exp $")
;;;
;;; **********************************************************************
;;;
;;; Package stuff and stuff like that.
;;;
;;; Re-Written by Rob MacLachlan. Earlier version written by
;;; Lee Schumacher. Apropos & iteration macros courtesy of Skef Wholey.
;;; Defpackage by Dan Zigmond. With-Package-Iterator by Blaine Burks.
;;;
(in-package 'lisp)
(export '(package packagep *package* make-package in-package find-package
package-name package-nicknames rename-package
package-use-list package-used-by-list package-shadowing-symbols
list-all-packages intern find-symbol unintern export
unexport import shadowing-import shadow use-package
unuse-package find-all-symbols do-symbols with-package-iterator
do-external-symbols do-all-symbols apropos apropos-list defpackage))
(in-package "EXTENSIONS")
(export '(*keyword-package* *lisp-package*))
(in-package 'lisp)
(defstruct (package
(:constructor internal-make-package)
(:predicate packagep)
(:print-function
(lambda (s stream d)
(declare (ignore d) (stream stream))
(multiple-value-bind (iu it) (internal-symbol-count s)
(multiple-value-bind (eu et) (external-symbol-count s)
(format stream
"#<The ~A package, ~D/~D internal, ~D/~D external>"
(package-%name s) iu it eu et)))))
(:make-load-form-fun
(lambda (package)
(values `(package-or-lose ',(package-name package))
nil))))
"Standard structure for the description of a package. Consists of
a list of all hash tables, the name of the package, the nicknames of
the package, the use-list for the package, the used-by- list, hash-
tables for the internal and external symbols, and a list of the
shadowing symbols."
(tables (list nil)) ; A list of all the hashtables for inherited symbols.
%name ; The string name of the package.
%nicknames ; List of nickname strings.
(%use-list ()) ; List of packages we use.
(%used-by-list ()) ; List of packages that use this package.
internal-symbols ; Hashtable of internal symbols.
external-symbols ; Hashtable of external symbols.
(%shadowing-symbols ())) ; List of shadowing symbols.
(macrolet ((frob (ext real)
`(defun ,ext (x) (,real (package-or-lose x)))))
(frob package-name package-%name)
(frob package-nicknames package-%nicknames)
(frob package-use-list package-%use-list)
(frob package-used-by-list package-%used-by-list)
(frob package-shadowing-symbols package-%shadowing-symbols))
(defvar *package* () "The current package.")
;;; An equal hashtable from package names to packages.
;;;
(defvar *package-names* (make-hash-table :test #'equal))
;;; Lots of people want the keyword package and Lisp package without a lot
;;; of fuss, so we give them their own variables.
;;;
(defvar *lisp-package*)
(defvar *keyword-package*)
;;; This magical variable is T during initialization so Use-Package's of packages
;;; that don't yet exist quietly win. Such packages are thrown onto the list
;;; *Deferred-Use-Packages* so that this can be fixed up later.
(defvar *in-package-init* nil)
(defvar *deferred-use-packages* nil)
;;; Find-Package -- Public
;;;
;;;
(defun find-package (name)
"Find the package having the specified name."
(values (gethash (string name) *package-names*)))
;;; Package-Listify -- Internal
;;;
;;; Return a list of packages given a package-or-string-or-symbol or
;;; list thereof, or die trying.
;;;
(defun package-listify (thing)
(let ((res ()))
(dolist (thing (if (listp thing) thing (list thing)) res)
(push (package-or-lose thing) res))))
;;; Package-Or-Lose -- Internal
;;;
;;; Take a package-or-string-or-symbol and return a package.
;;;
(defun package-or-lose (thing)
(if (packagep thing)
thing
(let ((thing (string thing)))
(cond ((gethash thing *package-names*))
(t
(cerror "Make this package."
"~S is not the name of a package." thing)
(make-package thing))))))
;;;; Package-Hashtables
;;;
;;; Packages are implemented using a special kind of hashtable. It is
;;; an open hashtable with a parallel 8-bit I-vector of hash-codes. The
;;; primary purpose of the hash for each entry is to reduce paging by
;;; allowing collisions and misses to be detected without paging in the
;;; symbol and pname for an entry. If the hash for an entry doesn't
;;; match that for the symbol that we are looking for, then we can
;;; go on without touching the symbol, pname, or even hastable vector.
;;; It turns out that, contrary to my expectations, paging is a very
;;; important consideration the design of the package representation.
;;; Using a similar scheme without the entry hash, the fasloader was
;;; spending more than half its time paging in INTERN.
;;; The hash code also indicates the status of an entry. If it zero,
;;; the the entry is unused. If it is one, then it is deleted.
;;; Double-hashing is used for collision resolution.
(defstruct (package-hashtable
(:constructor internal-make-package-hashtable ())
(:copier nil)
(:print-function
(lambda (table stream d)
(declare (ignore d))
(format stream
"#<Package-Hashtable: Size = ~D, Free = ~D, Deleted = ~D>"
(package-hashtable-size table)
(package-hashtable-free table)
(package-hashtable-deleted table)))))
table ; The g-vector of symbols.
hash ; The i-vector of pname hash values.
size ; The maximum number of entries allowed.
free ; The entries that can be made before we have to rehash.
deleted) ; The number of deleted entries.
;;; The maximum density we allow in a package hashtable.
;;;
(defparameter package-rehash-threshold 3/4)
;;; Entry-Hash -- Internal
;;;
;;; Compute a number from the sxhash of the pname and the length which
;;; must be between 2 and 255.
;;;
(defmacro entry-hash (length sxhash)
`(the fixnum (+ (the fixnum (rem (the fixnum (logxor ,length
,sxhash
(the fixnum (ash ,sxhash -8))
(the fixnum (ash ,sxhash -16))
(the fixnum (ash ,sxhash -19))))
254))
2)))
;;; Make-Package-Hashtable -- Internal
;;;
;;; Make a package hashtable having a prime number of entries at least
;;; as great as (/ size package-rehash-threshold). If Res is supplied,
;;; then it is destructively modified to produce the result. This is
;;; useful when changing the size, since there are many pointers to
;;; the hashtable.
;;;
(defun make-package-hashtable (size &optional
(res (internal-make-package-hashtable)))
(do ((n (logior (truncate size package-rehash-threshold) 1)
(+ n 2)))
((primep n)
(setf (package-hashtable-table res)
(make-array n))
(setf (package-hashtable-hash res)
(make-array n :element-type '(unsigned-byte 8) :initial-element 0))
(let ((size (truncate (* n package-rehash-threshold))))
(setf (package-hashtable-size res) size)
(setf (package-hashtable-free res) size))
(setf (package-hashtable-deleted res) 0)
res)
(declare (fixnum n))))
;;; Internal-Symbol-Count, External-Symbols-Count -- Internal
;;;
;;; Return internal and external symbols. Used by Genesis and stuff.
;;;
(flet ((stuff (table)
(let ((size (the fixnum (- (the fixnum (package-hashtable-size table))
(the fixnum (package-hashtable-deleted table))))))
(declare (fixnum size))
(values (the fixnum (- size (the fixnum (package-hashtable-free table)))) size))))
(defun internal-symbol-count (package)
(stuff (package-internal-symbols package)))
(defun external-symbol-count (package)
(stuff (package-external-symbols package))))
;;; Add-Symbol -- Internal
;;;
;;; Add a symbol to a package hashtable. The symbol is assumed
;;; not to be present.
;;;
(defun add-symbol (table symbol)
(let* ((vec (package-hashtable-table table))
(hash (package-hashtable-hash table))
(len (length vec))
(sxhash (%sxhash-simple-string (symbol-name symbol)))
(h2 (the fixnum (1+ (the fixnum (rem sxhash
(the fixnum (- len 2))))))))
(declare (simple-vector vec)
(type (simple-array (unsigned-byte 8)) hash)
(fixnum len sxhash h2))
(cond ((zerop (the fixnum (package-hashtable-free table)))
(make-package-hashtable (the fixnum
(* (the fixnum
(package-hashtable-size table))
2))
table)
(add-symbol table symbol)
(dotimes (i len)
(declare (fixnum i))
(when (> (the fixnum (aref hash i)) 1)
(add-symbol table (svref vec i)))))
(t
(do ((i (rem sxhash len) (rem (+ i h2) len)))
((< (the fixnum (aref hash i)) 2)
(if (zerop (the fixnum (aref hash i)))
(decf (the fixnum (package-hashtable-free table)))
(decf (the fixnum (package-hashtable-deleted table))))
(setf (svref vec i) symbol)
(setf (aref hash i)
(entry-hash (length (the simple-string (symbol-name symbol)))
sxhash)))
(declare (fixnum i)))))))
;;; With-Symbol -- Internal
;;;
;;; Find where the symbol named String is stored in Table. Index-Var
;;; is bound to the index, or NIL if it is not present. Symbol-Var
;;; is bound to the symbol. Length and Hash are the length and sxhash
;;; of String. Entry-Hash is the entry-hash of the string and length.
;;;
(defmacro with-symbol ((index-var symbol-var table string length sxhash
entry-hash)
&body forms)
(let ((vec (gensym)) (hash (gensym)) (len (gensym)) (h2 (gensym))
(name (gensym)) (name-len (gensym)) (ehash (gensym)))
`(let* ((,vec (package-hashtable-table ,table))
(,hash (package-hashtable-hash ,table))
(,len (length ,vec))
(,h2 (1+ (the fixnum (rem (the fixnum ,sxhash)
(the fixnum (- ,len 2)))))))
(declare (type (simple-array (unsigned-byte 8) (*)) ,hash)
(simple-vector ,vec)
(fixnum ,len ,h2))
(prog ((,index-var (rem (the fixnum ,sxhash) ,len))
,symbol-var ,ehash)
(declare (type (or fixnum null) ,index-var))
LOOP
(setq ,ehash (aref ,hash ,index-var))
(cond ((eql ,ehash ,entry-hash)
(setq ,symbol-var (svref ,vec ,index-var))
(let* ((,name (symbol-name ,symbol-var))
(,name-len (length ,name)))
(declare (simple-string ,name)
(fixnum ,name-len))
(when (and (= ,name-len ,length)
(string= ,string ,name :end1 ,length
:end2 ,name-len))
(go DOIT))))
((zerop ,ehash)
(setq ,index-var nil)
(go DOIT)))
(setq ,index-var (+ ,index-var ,h2))
(when (>= ,index-var ,len)
(setq ,index-var (- ,index-var ,len)))
(go LOOP)
DOIT
(return (progn ,@forms))))))
;;; Nuke-Symbol -- Internal
;;;
;;; Delete the entry for String in Table. The entry must exist.
;;;
(defun nuke-symbol (table string)
(declare (simple-string string))
(let* ((length (length string))
(hash (%sxhash-simple-string string))
(ehash (entry-hash length hash)))
(declare (fixnum length hash))
(with-symbol (index symbol table string length hash ehash)
(setf (aref (package-hashtable-hash table) index) 1)
(setf (aref (package-hashtable-table table) index) nil)
(incf (package-hashtable-deleted table)))))
;;;; Iteration macros.
;;; Instead of using slow, silly successor functions, we make the iteration
;;; guys be big PROG's. Yea!
(eval-when (compile load eval)
(defun make-do-symbols-vars ()
`(,(gensym) ; index
,(gensym) ; hash
,(gensym) ; hash-vector
,(gensym))) ; terminus
(defun make-do-symbols-code (vars var hash-table exit-form forms)
(let ((index (first vars))
(hash-vector (second vars))
(hash (third vars))
(terminus (fourth vars))
(TOP (gensym)))
`((setq ,index 0)
(setq ,hash-vector (package-hashtable-table ,hash-table))
(setq ,hash (package-hashtable-hash ,hash-table))
(setq ,terminus (length (the simple-vector ,hash-vector)))
,TOP
(if (= (the fixnum ,index) (the fixnum ,terminus))
,exit-form)
(when (> (the fixnum (aref (the (simple-array (unsigned-byte 8)) ,hash)
,index))
1)
(setq ,var (svref ,hash-vector ,index))
,@forms)
(incf ,index)
(go ,TOP))))
); eval-when (compile load eval)
(defmacro do-symbols ((var &optional (package '*package*) result-form)
&body (code decls))
"Do-Symbols (Var [Package [Result-Form]]) {Declaration}* {Tag | Statement}*
Executes the Forms at least once for each symbol accessible in the given
Package with Var bound to the current symbol."
(let* ((DONE-INTERNAL (gensym))
(DONE-EXTERNAL (gensym))
(NEXT-INHERIT (gensym))
(vars (make-do-symbols-vars))
(n-package (gensym))
(shadowed (gensym))
(inherits (gensym))
(this-inherit (gensym)))
`(prog* ((,n-package (package-or-lose ,package))
(,shadowed (package-%shadowing-symbols ,n-package))
(,inherits (package-%use-list ,n-package))
,var ,@vars ,this-inherit)
,@decls
,@(make-do-symbols-code
vars var `(package-internal-symbols ,n-package)
`(go ,DONE-INTERNAL)
code)
,DONE-INTERNAL
,@(make-do-symbols-code
vars var `(package-external-symbols ,n-package)
`(go ,DONE-EXTERNAL)
code)
,DONE-EXTERNAL
,NEXT-INHERIT
(when (null ,inherits)
(setq ,var nil)
(return ,result-form))
(setq ,this-inherit (package-external-symbols (car ,inherits)))
,@(make-do-symbols-code
vars var this-inherit
`(progn
(setq ,inherits (cdr ,inherits))
(go ,NEXT-INHERIT))
`((when (or (not ,shadowed)
(eq (find-symbol (symbol-name ,var) ,n-package) ,var))
,@code))))))
(defmacro do-external-symbols ((var &optional (package '*package*) result-form)
&body (code decls))
"Do-External-Symbols (Var [Package [Result-Form]])
{Declaration}* {Tag | Statement}*
Executes the Forms once for each external symbol in the given Package with
Var bound to the current symbol."
(let ((vars (make-do-symbols-vars))
(n-package (gensym)))
`(prog ((,n-package (package-or-lose ,package))
,var ,@vars)
,@decls
,@(make-do-symbols-code
vars var `(package-external-symbols ,n-package)
`(return (progn (setq ,var nil) ,result-form))
code))))
(defmacro do-all-symbols ((var &optional result-form)
&body (code decls))
"Do-All-Symbols (Var [Result-Form]) {Declaration}* {Tag | Statement}*
Executes the Forms once for each symbol in each package with Var bound
to the current symbol."
(let* ((PACKAGE-LOOP (gensym))
(TAG (gensym))
(package-list (gensym))
(vars (make-do-symbols-vars))
(internal-code (make-do-symbols-code
vars var `(package-internal-symbols (car ,package-list))
`(go ,TAG)
code))
(external-code (make-do-symbols-code
vars var `(package-external-symbols (car ,package-list))
`(progn (setq ,package-list (cdr ,package-list))
(go ,PACKAGE-LOOP))
code)))
`(prog (,package-list ,var ,@vars)
,@decls
(setq ,package-list (list-all-packages))
,PACKAGE-LOOP
(when (null ,package-list)
(setq ,var nil)
(return ,result-form))
,@internal-code
,TAG
,@external-code)))
;;;; WITH-PACKAGE-ITERATOR
(defmacro with-package-iterator ((mname package-list &rest symbol-types)
&body body)
(let* ((packages (gensym))
(these-packages (gensym))
(ordered-types (let ((res nil))
(dolist (kind '(:inherited :external :internal)
res)
(when (member kind symbol-types)
(push kind res))))) ; Order symbol-types.
(counter (gensym))
(kind (gensym))
(hash-vector (gensym))
(vector (gensym))
(package-use-list (gensym))
(init-macro (gensym))
(end-test-macro (gensym))
(real-symbol-p (gensym))
(BLOCK (gensym)))
`(let* ((,these-packages ,package-list)
(,packages `,(mapcar #'(lambda (package)
(if (packagep package)
package
(find-package package)))
(if (consp ,these-packages)
,these-packages
(list ,these-packages))))
(,counter nil)
(,kind (car ,packages))
(,hash-vector nil)
(,vector nil)
(,package-use-list nil))
,(if (member :inherited ordered-types)
`(setf ,package-use-list (package-%use-list (car ,packages)))
`(declare (ignore ,package-use-list)))
(macrolet ((,init-macro (next-kind)
(let ((symbols (gensym)))
`(progn
(setf ,',kind ,next-kind)
(setf ,',counter nil)
,(case next-kind
(:internal
`(let ((,symbols (package-internal-symbols
(car ,',packages))))
(setf ,',vector (package-hashtable-table ,symbols))
(setf ,',hash-vector (package-hashtable-hash ,symbols))))
(:external
`(let ((,symbols (package-external-symbols
(car ,',packages))))
(setf ,',vector (package-hashtable-table ,symbols))
(setf ,',hash-vector (package-hashtable-hash ,symbols))))
(:inherited
`(let ((,symbols (package-external-symbols
(car ,',package-use-list))))
(setf ,',vector (package-hashtable-table ,symbols))
(setf ,',hash-vector (package-hashtable-hash ,symbols))))))))
(,end-test-macro (this-kind)
`,(let ((next-kind (cadr (member this-kind
',ordered-types))))
(if next-kind
`(,',init-macro ,next-kind)
`(if (endp (setf ,',packages (cdr ,',packages)))
(return-from ,',BLOCK)
(,',init-macro ,(car ',ordered-types)))))))
(when ,packages
,(when (null symbol-types)
(error "Must supply at least one of :internal, :external, or ~
:inherited."))
,(dolist (symbol symbol-types)
(unless (member symbol '(:internal :external :inherited))
(error "~S is not one of :internal, :external, or :inherited."
symbol)))
(,init-macro ,(car ordered-types))
(flet ((,real-symbol-p (number)
(> number 1)))
(macrolet ((,mname ()
`(block ,',BLOCK
(loop
(case ,',kind
,@(when (member :internal ',ordered-types)
`((:internal
(setf ,',counter
(position-if #',',real-symbol-p ,',hash-vector
:start (if ,',counter
(1+ ,',counter)
0)))
(if ,',counter
(return-from ,',BLOCK
(values t (svref ,',vector ,',counter)
,',kind (car ,',packages)))
(,',end-test-macro :internal)))))
,@(when (member :external ',ordered-types)
`((:external
(setf ,',counter
(position-if #',',real-symbol-p ,',hash-vector
:start (if ,',counter
(1+ ,',counter)
0)))
(if ,',counter
(return-from ,',BLOCK
(values t (svref ,',vector ,',counter)
,',kind (car ,',packages)))
(,',end-test-macro :external)))))
,@(when (member :inherited ',ordered-types)
`((:inherited
(setf ,',counter
(position-if #',',real-symbol-p ,',hash-vector
:start (if ,',counter
(1+ ,',counter)
0)))
(cond (,',counter
(return-from
,',BLOCK
(values t (svref ,',vector ,',counter)
,',kind (car ,',packages))))
(t
(setf ,',package-use-list
(cdr ,',package-use-list))
(cond ((endp ,',package-use-list)
(setf ,',packages (cdr ,',packages))
(when (endp ,',packages)
(return-from ,',BLOCK))
(setf ,',package-use-list
(package-%use-list
(car ,',packages)))
(,',init-macro ,(car ',ordered-types)))
(t (,',init-macro :inherited)
(setf ,',counter nil)))))))))))))
,@body)))))))
;;;; DEFPACKAGE:
(defmacro defpackage (package &rest arguments)
"Defines a new package called PACKAGE. ARGUMENTS should a list of forms,
each of with is one of:
(:SIZE <integer>)
(:NICKNAMES {package-name}*)
(:SHADOW {symbol-name}*)
(:SHADOWING-IMPORT-FROM <package-name> {symbol-name}*)
(:USE {package-name}*)
(:IMPORT-FROM <package-name> {symbol-name}*)
(:INTERN {symbol-name}*)
(:EXPORT {symbol-name}*)
All keywords except :SIZE can be used multiple times."
(let ((body nil)
(package-name
(etypecase package
;; Make sure we have a good package name to use.
(string package)
(symbol (symbol-name package)))))
(multiple-value-bind
(nicknames uses shadows imports shadowed-imports exports interns size)
(parse-defpackage-keywords arguments package-name)
;; We set up the body of the form to return first things first
;; for readability, even though (since we're using PUSH) we
;; then have to NREVERSE at the end. The order of operations
;; must be: 1. :shadow and :shadowing-import-from
;; 2. :use
;; 3. :import-from and :return
;; 4. :export
(when shadows
(push `(shadow (list ,@shadows) ,package-name)
body))
(when shadowed-imports
(push `(shadowing-import (list ,@shadowed-imports) ,package-name)
body))
(when uses
(push `(use-package (list ,@uses) ,package-name)
body))
(when imports
(push `(import (list ,@imports) ,package-name)
body))
(when interns
(dolist (symbol interns)
(push `(intern ,symbol ,package-name)
body)))
(when exports
(push `(export (list ,@exports) ,package-name)
body))
;;
;; We do :nicknames and :sizeat the top (where it's convenient).
;; :Size is not implemented very well. We assume, for absolutely
;; no good reason, that approximates 1/5 of the symbols in a
;; package will be external.
`(progn
(eval-when (compile load eval)
(unless (find-package ,package-name)
(make-package
,package-name
,@(if nicknames `(:nicknames (list ,@nicknames)))
,@(if size `(:internal-symbols ,(round size 5/4)
:external-symbols ,(round size 5))))))
,@(nreverse body)
(find-package ,package-name)))))
(defun parse-defpackage-keywords (rest-list n-package)
"Parses the arguments to DEFPACKAGE. Returns eight arguments:
1. A list of the package's nicknames.
2. A list of the other packages that this package uses.
3. A list of shadows.
4. A list of lists of the form (package-name {symbol-name}*)
describing the symbols to be imported from package-name
and placed on the shadowed symbols list.
5. A list of lists as above of symbols to be imported.
6. A list of symbols to export.
7. A list of symbols to intern.
8. The declared size of the package.
Nil is returned as any of these eight values if no value is provided
by the user. Only mimimal error checking is done here."
(do* ((symbols-in nil)
(symbols-out)
(nicknames nil)
(uses nil)
(shadows nil)
(imports nil)
(shadowed-imports nil)
(exports nil)
(interns nil)
(size nil)
(remaining-args rest-list (rest remaining-args))
(current-keyword (first (first remaining-args))
(first (first remaining-args)))
(current-args (rest (first remaining-args))
(rest (first remaining-args))))
((endp remaining-args)
(values nicknames
uses
shadows
imports
shadowed-imports
exports
interns
size))
(case current-keyword
(:nicknames
(setf nicknames (append nicknames (stringify-symbols current-args))))
(:use
(setf uses (append uses (stringify-symbols current-args))))
(:shadow
(setf current-args (stringify-symbols current-args))
(setf symbols-in (append-but-lose-if-overlap symbols-in current-args))
(dolist (string current-args)
(push string shadows)))
(:shadowing-import-from
(setf current-args (stringify-symbols current-args))
(setf symbols-in (append-but-lose-if-overlap symbols-in
(rest current-args)))
(dolist (string (rest current-args))
(push `(find-symbol-or-lose ,string ,(first current-args))
shadowed-imports)))
(:import-from
(setf current-args (stringify-symbols current-args))
(setf symbols-in (append-but-lose-if-overlap symbols-in
(rest current-args)))
(dolist (string (rest current-args))
(push `(find-symbol-or-lose ,string ,(first current-args))
imports)))
(:export
(setf symbols-out (append-but-lose-if-overlap symbols-out
(rest current-args)))
(dolist (string (stringify-symbols current-args))
(push `(intern ,string ,n-package)
exports)))
(:intern
(setf current-args (stringify-symbols current-args))
(setf symbols-in (append-but-lose-if-overlap symbols-in current-args))
(setf symbols-out (append-but-lose-if-overlap symbols-out current-args))
(setf interns (append interns current-args)))
(:size
(if (null size)
(if (= (length current-args) 1)
(setf size (first current-args))
(error "Too many arguments to :SIZE keyword in DEFPACAKGE."))
(error ":SIZE keyword used more than once in DEFPACKAGE.")))
(otherwise
(error "Bad keyword passed to DEFPACKAGE: ~S." current-keyword)))))
(defun find-symbol-or-lose (symbol package)
"Tries to find SYMBOL in PACKAGE, but signals a continuable error if
it's not there."
(multiple-value-bind (sym how)
(find-symbol symbol package)
(cond ((not how)
(cerror "INTERN this symbol."
"Can't find the symbol named ~S in ~S."
symbol package)
(values (intern symbol package)))
(t sym))))
(defun stringify-symbols (symbols)
"Takes a list of symbols and/or strings and returns a list of
strings using SYMBOL-NAME for any necessary coersion."
(mapcar #'(lambda (x)
(etypecase x
(string x)
(symbol (symbol-name x))))
symbols))
(defun append-but-lose-if-overlap (list-one list-two &key (test #'string=))
"APPENDs two lists but screams if they intersect at all.
Uses STRING= as default test because that's what DEFPACKAGE wants to use."
(if (intersection list-one list-two :test test)
(error "Overlap found in argument lists.")
(append list-one list-two)))
;;; Enter-New-Nicknames -- Internal
;;;
;;; Enter any new Nicknames for Package into *package-names*.
;;; If there is a conflict then give the user a chance to do
;;; something about it.
;;;
(defun enter-new-nicknames (package nicknames)
(check-type nicknames list)
(dolist (n nicknames)
(let* ((n (string n))
(found (gethash n *package-names*)))
(cond ((not found)
(setf (gethash n *package-names*) package)
(push n (package-%nicknames package)))
((eq found package))
((string= (package-%name found) n)
(cerror "Ignore this nickname."
"~S is a package name, so it cannot be a nickname for ~S."
n (package-%name package)))
(t
(cerror "Redefine this nickname."
"~S is already a nickname for ~S."
n (package-%name found))
(setf (gethash n *package-names*) package)
(push n (package-%nicknames package)))))))
;;; Make-Package -- Public
;;;
;;; Check for package name conflicts in name and nicknames, then
;;; make the package. Do a use-package for each thing in the use list
;;; so that checking for conflicting exports among used packages is done.
;;;
(defun make-package (name &key (use '("LISP")) nicknames
(internal-symbols 10) (external-symbols 10))
"Makes a new package having the specified Name and Nicknames. The
package will inherit all external symbols from each package in
the use list. :Internal-Symbols and :External-Symbols are
estimates for the number of internal and external symbols which
will ultimately be present in the package."
(when (find-package name)
(error "A package named ~S already exists" name))
(let* ((name (string name))
(package (internal-make-package
:%name name
:internal-symbols (make-package-hashtable internal-symbols)
:external-symbols (make-package-hashtable external-symbols))))
(if *in-package-init*
(push (list use package) *deferred-use-packages*)
(use-package use package))
(enter-new-nicknames package nicknames)
(setf (gethash name *package-names*) package)))
;;; In-Package -- Public
;;;
;;; Like Make-Package, only different.
;;;
(defun in-package (name &rest keys &key nicknames use)
"Sets *package* to package with given name, creating the package if
it does not exist. If the package already exists then it is modified
to agree with the :Use and :Nicknames arguments. Any new nicknames
are added without removing any old ones not specified. If any package
in the :Use list is not currently used, then it is added to the use
list."
(let ((package (find-package name)))
(cond
(package
(if *in-package-init*
(push (list use package) *deferred-use-packages*)
(use-package use package))
(enter-new-nicknames package nicknames)
(setq *package* package))
(t
(setq *package* (apply #'make-package name keys))))))
;;; Rename-Package -- Public
;;;
;;; Change the name if we can, blast any old nicknames and then
;;; add in any new ones.
;;;
(defun rename-package (package name &optional (nicknames ()))
"Changes the name and nicknames for a package."
(let* ((package (package-or-lose package))
(name (string name))
(found (find-package name)))
(unless (or (not found) (eq found package))
(error "A package named ~S already exists." name))
(remhash (package-%name package) *package-names*)
(dolist (n (package-%nicknames package))
(remhash n *package-names*))
(setf (package-%name package) name)
(setf (gethash name *package-names*) package)
(setf (package-%nicknames package) ())
(enter-new-nicknames package nicknames)
package))
;;; List-All-Packages -- Public
;;;
;;;
(defun list-all-packages ()
"Returns a list of all existing packages."
(let ((res ()))
(maphash #'(lambda (k v)
(declare (ignore k))
(pushnew v res))
*package-names*)
res))
;;; Intern -- Public
;;;
;;; Simple-stringify the name and call intern*.
;;;
(defun intern (name &optional package)
"Returns a symbol having the specified name, creating it if necessary."
(let ((name (if (simple-string-p name) name (coerce name 'simple-string))))
(declare (simple-string name))
(intern* name (length name)
(if package (package-or-lose package) *package*))))
;;; Find-Symbol -- Public
;;;
;;; Ditto.
;;;
(defun find-symbol (name &optional package)
"Returns the symbol named String in Package. If such a symbol is found
then the second value is :internal, :external or :inherited to indicate
how the symbol is accessible. If no symbol is found then both values
are NIL."
(let ((name (if (simple-string-p name) name (coerce name 'simple-string))))
(declare (simple-string name))
(find-symbol* name (length name)
(if package (package-or-lose package) *package*))))
;;; Intern* -- Internal
;;;
;;; If the symbol doesn't exist then create it, special-casing
;;; the keyword package.
;;;
(defun intern* (name length package)
(declare (simple-string name))
(multiple-value-bind (symbol where) (find-symbol* name length package)
(if where
(values symbol where)
(let ((symbol (make-symbol (subseq name 0 length))))
(%set-symbol-package symbol package)
(cond ((eq package *keyword-package*)
(add-symbol (package-external-symbols package) symbol)
(%set-symbol-value symbol symbol))
(t
(add-symbol (package-internal-symbols package) symbol)))
(values symbol nil)))))
;;; Find-Symbol* -- Internal
;;;
;;; Check internal and external symbols, then scan down the list
;;; of hashtables for inherited symbols. When an inherited symbol
;;; is found pull that table to the beginning of the list.
;;;
(defun find-symbol* (string length package)
(declare (simple-string string)
(fixnum length))
(let* ((hash (%sxhash-simple-substring string length))
(ehash (entry-hash length hash)))
(declare (fixnum hash ehash))
(with-symbol (found symbol (package-internal-symbols package)
string length hash ehash)
(when found
(return-from find-symbol* (values symbol :internal))))
(with-symbol (found symbol (package-external-symbols package)
string length hash ehash)
(when found
(return-from find-symbol* (values symbol :external))))
(let ((head (package-tables package)))
(do ((prev head table)
(table (cdr head) (cdr table)))
((null table) (values nil nil))
(with-symbol (found symbol (car table) string length hash ehash)
(when found
(unless (eq prev head)
(shiftf (cdr prev) (cdr table) (cdr head) table))
(return-from find-symbol* (values symbol :inherited))))))))
;;; Find-External-Symbol -- Internal
;;;
;;; Similar to Find-Symbol, but only looks for an external symbol.
;;; This is used for fast name-conflict checking in this file and symbol
;;; printing in the printer.
;;;
(defun find-external-symbol (string package)
(declare (simple-string string))
(let* ((length (length string))
(hash (%sxhash-simple-string string))
(ehash (entry-hash length hash)))
(declare (fixnum length hash))
(with-symbol (found symbol (package-external-symbols package)
string length hash ehash)
(values symbol found))))
;;; Unintern -- Public
;;;
;;; If we are uninterning a shadowing symbol, then a name conflict can
;;; result, otherwise just nuke the symbol.
;;;
(defun unintern (symbol &optional (package *package*))
"Makes Symbol no longer present in Package. If Symbol was present
then T is returned, otherwise NIL. If Package is Symbol's home
package, then it is made uninterned."
(let* ((package (package-or-lose package))
(name (symbol-name symbol))
(shadowing-symbols (package-%shadowing-symbols package)))
(declare (list shadowing-symbols) (simple-string name))
;;
;; If a name conflict is revealed, give use a chance to shadowing-import
;; one of the accessible symbols.
(when (member symbol shadowing-symbols)
(let ((cset ()))
(dolist (p (package-%use-list package))
(multiple-value-bind (s w) (find-external-symbol name p)
(when w (pushnew s cset))))
(when (cdr cset)
(loop
(cerror
"prompt for a symbol to shadowing-import."
"Uninterning symbol ~S causes name conflict among these symbols:~%~S"
symbol cset)
(write-string "Symbol to shadowing-import: " *query-io*)
(let ((sym (read *query-io*)))
(cond
((not (symbolp sym))
(format *query-io* "~S is not a symbol."))
((not (member sym cset))
(format *query-io* "~S is not one of the conflicting symbols."))
(t
(shadowing-import sym package)
(return-from unintern t)))))))
(setf (package-%shadowing-symbols package)
(remove symbol shadowing-symbols)))
(multiple-value-bind (s w) (find-symbol name package)
(declare (ignore s))
(cond ((or (eq w :internal) (eq w :external))
(nuke-symbol (if (eq w :internal)
(package-internal-symbols package)
(package-external-symbols package))
name)
(if (eq (symbol-package symbol) package)
(%set-symbol-package symbol nil))
t)
(t nil)))))
;;; Symbol-Listify -- Internal
;;;
;;; Take a symbol-or-list-of-symbols and return a list, checking types.
;;;
(defun symbol-listify (thing)
(cond ((listp thing)
(dolist (s thing)
(unless (symbolp s) (error "~S is not a symbol." s)))
thing)
((symbolp thing) (list thing))
(t
(error "~S is neither a symbol nor a list of symbols." thing))))
;;; Moby-Unintern -- Internal
;;;
;;; Like Unintern, but if symbol is inherited chases down the
;;; package it is inherited from and uninterns it there. Used
;;; for name-conflict resolution. Shadowing symbols are not
;;; uninterned since they do not cause conflicts.
;;;
(defun moby-unintern (symbol package)
(unless (member symbol (package-%shadowing-symbols package))
(or (unintern symbol package)
(let ((name (symbol-name symbol)))
(multiple-value-bind (s w) (find-symbol name package)
(declare (ignore s))
(when (eq w :inherited)
(dolist (q (package-%use-list package))
(multiple-value-bind (u x) (find-external-symbol name q)
(declare (ignore u))
(when x
(unintern symbol q)
(return t))))))))))
;;; Export -- Public
;;;
;;; Do more stuff.
;;;
(defun export (symbols &optional (package *package*))
"Exports Symbols from Package, checking that no name conflicts result."
(let ((package (package-or-lose package))
(syms ()))
;;
;; Punt any symbols that are already external.
(dolist (sym (symbol-listify symbols))
(multiple-value-bind (s w)
(find-external-symbol (symbol-name sym) package)
(declare (ignore s))
(unless (or w (member sym syms)) (push sym syms))))
;;
;; Find symbols and packages with conflicts.
(let ((used-by (package-%used-by-list package))
(cpackages ())
(cset ()))
(dolist (sym syms)
(let ((name (symbol-name sym)))
(dolist (p used-by)
(multiple-value-bind (s w) (find-symbol name p)
(when (and w (not (eq s sym))
(not (member s (package-%shadowing-symbols p))))
(pushnew sym cset)
(pushnew p cpackages))))))
(when cset
(restart-case
(error "Exporting these symbols from the ~A package:~%~S~%~
results in name conflicts with these packages:~%~{~A ~}"
(package-%name package) cset (mapcar #'package-%name cpackages))
(unintern-conflicting-symbols ()
:report "Unintern conflicting symbols."
(dolist (p cpackages)
(dolist (sym cset)
(moby-unintern sym p))))
(skip-exporting-these-symbols ()
:report "Skip exporting conflicting symbols."
(setq syms (nset-difference syms cset))))))
;;
;; Check that all symbols are accessible. If not, ask to import them.
(let ((missing ())
(imports ()))
(dolist (sym syms)
(multiple-value-bind (s w) (find-symbol (symbol-name sym) package)
(cond ((not (and w (eq s sym))) (push sym missing))
((eq w :inherited) (push sym imports)))))
(when missing
(cerror "Import these symbols into the ~A package."
"These symbols are not accessible in the ~A package:~%~S"
(package-%name package) missing)
(import missing package))
(import imports package))
;;
;; And now, three pages later, we export the suckers.
(let ((internal (package-internal-symbols package))
(external (package-external-symbols package)))
(dolist (sym syms)
(nuke-symbol internal (symbol-name sym))
(add-symbol external sym)))
t))
;;; Unexport -- Public
;;;
;;; Check that all symbols are accessible, then move from external to
;;; internal.
;;;
(defun unexport (symbols &optional (package *package*))
"Makes Symbols no longer exported from Package."
(let ((package (package-or-lose package))
(syms ()))
(dolist (sym (symbol-listify symbols))
(multiple-value-bind (s w) (find-symbol (symbol-name sym) package)
(cond ((or (not w) (not (eq s sym)))
(error "~S is not accessible in the ~A package."
sym (package-%name package)))
((eq w :external) (pushnew sym syms)))))
(let ((internal (package-internal-symbols package))
(external (package-external-symbols package)))
(dolist (sym syms)
(add-symbol internal sym)
(nuke-symbol external (symbol-name sym))))
t))
;;; Import -- Public
;;;
;;; Check for name conflic caused by the import and let the user
;;; shadowing-import if there is.
;;;
(defun import (symbols &optional (package *package*))
"Make Symbols accessible as internal symbols in Package. If a symbol
is already accessible then it has no effect. If a name conflict
would result from the importation, then a correctable error is signalled."
(let ((package (package-or-lose package))
(symbols (symbol-listify symbols))
(syms ())
(cset ()))
(dolist (sym symbols)
(multiple-value-bind (s w) (find-symbol (symbol-name sym) package)
(cond ((not w)
(let ((found (member sym syms :test #'string=)))
(if found
(when (not (eq (car found) sym))
(push sym cset))
(push sym syms))))
((not (eq s sym)) (push sym cset))
((eq w :inherited) (push sym syms)))))
(when cset
(cerror
"Import these symbols with Shadowing-Import."
"Importing these symbols into the ~A package causes a name conflict:~%~S"
(package-%name package) cset))
;;
;; Add the new symbols to the internal hashtable.
(let ((internal (package-internal-symbols package)))
(dolist (sym syms)
(add-symbol internal sym)))
;;
;; If any of the symbols are uninterned, make them be owned by Package.
(dolist (sym symbols)
(unless (symbol-package sym) (%set-symbol-package sym package)))
(shadowing-import cset package)))
;;; Shadowing-Import -- Public
;;;
;;; If a conflicting symbol is present, unintern it, otherwise just
;;; stick the symbol in.
;;;
(defun shadowing-import (symbols &optional (package *package*))
"Import Symbols into package, disregarding any name conflict. If
a symbol of the same name is present, then it is uninterned.
The symbols are added to the Package-Shadowing-Symbols."
(let* ((package (package-or-lose package))
(internal (package-internal-symbols package)))
(dolist (sym (symbol-listify symbols))
(multiple-value-bind (s w) (find-symbol (symbol-name sym) package)
(unless (and w (not (eq w :inherited)) (eq s sym))
(when (or (eq w :internal) (eq w :external))
;;
;; If it was shadowed, we don't want Unintern to flame out...
(setf (package-%shadowing-symbols package)
(remove s (the list (package-%shadowing-symbols package))))
(unintern s package))
(add-symbol internal sym))
(pushnew sym (package-%shadowing-symbols package)))))
t)
;;; Shadow -- Public
;;;
;;;
(defun shadow (symbols &optional (package *package*))
"Make an internal symbol in Package with the same name as each of the
specified symbols, adding the new symbols to the Package-Shadowing-Symbols.
If a symbol with the given name is already present in Package, then
the existing symbol is placed in the shadowing symbols list if it is
not already present."
(let* ((package (package-or-lose package))
(internal (package-internal-symbols package)))
(dolist (name (mapcar #'string
(if (listp symbols) symbols (list symbols))))
(multiple-value-bind (s w) (find-symbol name package)
(when (or (not w) (eq w :inherited))
(setq s (make-symbol name))
(%set-symbol-package s package)
(add-symbol internal s))
(pushnew s (package-%shadowing-symbols package))))))
t)
;;; Use-Package -- Public
;;;
;;; Do stuff to use a package, with all kinds of fun name-conflict
;;; checking.
;;;
(defun use-package (packages-to-use &optional (package *package*))
"Add all the Package-To-Use to the use list for Package so that
the external symbols of the used packages are accessible as internal
symbols in Package."
(let ((packages (package-listify packages-to-use))
(package (package-or-lose package)))
;;
;; Loop over each package, use'ing one at a time...
(dolist (pkg packages)
(unless (member pkg (package-%use-list package))
(let ((cset ())
(shadowing-symbols (package-%shadowing-symbols package))
(use-list (package-%use-list package)))
;;
;; If the number of symbols already accessible is less than the
;; number to be inherited then it is faster to run the test the
;; other way. This is particularly valuable in the case of
;; a new package use'ing Lisp.
(cond
((< (+ (internal-symbol-count package)
(external-symbol-count package)
(let ((res 0))
(dolist (p use-list res)
(incf res (external-symbol-count p)))))
(external-symbol-count pkg))
(do-symbols (sym package)
(multiple-value-bind (s w)
(find-external-symbol (symbol-name sym) pkg)
(when (and w (not (eq s sym))
(not (member sym shadowing-symbols)))
(push sym cset))))
(dolist (p use-list)
(do-external-symbols (sym p)
(multiple-value-bind (s w)
(find-external-symbol (symbol-name sym)
pkg)
(when (and w (not (eq s sym))
(not (member (find-symbol (symbol-name sym)
package)
shadowing-symbols)))
(push sym cset))))))
(t
(do-external-symbols (sym pkg)
(multiple-value-bind (s w)
(find-symbol (symbol-name sym) package)
(when (and w (not (eq s sym))
(not (member s shadowing-symbols)))
(push s cset))))))
(when cset
(cerror
"unintern the conflicting symbols in the ~2*~A package."
"Use'ing package ~A results in name conflicts for these symbols:~%~S"
(package-%name pkg) cset (package-%name package))
(dolist (s cset) (moby-unintern s package))))
(push pkg (package-%use-list package))
(push (package-external-symbols pkg) (cdr (package-tables package)))
(push package (package-%used-by-list pkg)))))
t)
;;; Unuse-Package -- Public
;;;
;;;
(defun unuse-package (packages-to-unuse &optional (package *package*))
"Remove Packages-To-Unuse from the use list for Package."
(let ((package (package-or-lose package)))
(dolist (p (package-listify packages-to-unuse))
(setf (package-%use-list package)
(remove p (the list (package-%use-list package))))
(setf (package-tables package)
(delete (package-external-symbols p)
(the list (package-tables package))))
(setf (package-%used-by-list p)
(remove package (the list (package-%used-by-list p)))))
t))
;;; Find-All-Symbols -- Public
;;;
;;;
(defun find-all-symbols (string-or-symbol)
"Return a list of all symbols in the system having the specified name."
(let ((string (string string-or-symbol))
(res ()))
(maphash #'(lambda (k v)
(declare (ignore k))
(multiple-value-bind (s w) (find-symbol string v)
(when w (pushnew s res))))
*package-names*)
res))
;;; Apropos and Apropos-List.
(defun briefly-describe-symbol (symbol)
(fresh-line)
(prin1 symbol)
(when (boundp symbol)
(write-string ", value: ")
(prin1 (symbol-value symbol)))
(if (fboundp symbol)
(write-string " (defined)")))
(defun apropos-search (symbol string)
(declare (simple-string string))
(do* ((index 0 (1+ index))
(name (symbol-name symbol))
(length (length string))
(terminus (- (length name) length)))
((> index terminus)
nil)
(declare (simple-string name)
(fixnum index terminus length))
(if (do ((jndex 0 (1+ jndex))
(kndex index (1+ kndex)))
((= jndex length)
t)
(declare (fixnum jndex kndex))
(let ((char (schar name kndex)))
(unless (char= (schar string jndex) (char-upcase char))
(return nil))))
(return t))))
(defun apropos (string &optional package external-only)
"Briefly describe all symbols which contain the specified String.
If Package is supplied then only describe symbols present in
that package. If External-Only is true then only describe
external symbols in the specified package."
(let ((string (string-upcase string)))
(declare (simple-string string))
(if (null package)
(do-all-symbols (symbol)
(if (apropos-search symbol string)
(briefly-describe-symbol symbol)))
(let ((package (package-or-lose package)))
(if external-only
(do-external-symbols (symbol package)
(if (apropos-search symbol string)
(briefly-describe-symbol symbol)))
(do-symbols (symbol package)
(if (apropos-search symbol string)
(briefly-describe-symbol symbol))))))
(values)))
(defun apropos-list (string &optional package external-only)
"Identical to Apropos, except that it returns a list of the symbols
found instead of describing them."
(let ((string (string-upcase string))
(list '()))
(declare (simple-string string))
(if (null package)
(do-all-symbols (symbol)
(if (apropos-search symbol string)
(push symbol list)))
(let ((package (package-or-lose package)))
(if external-only
(do-external-symbols (symbol package)
(if (apropos-search symbol string)
(push symbol list)))
(do-symbols (symbol package)
(if (apropos-search symbol string)
(push symbol list))))))
list))
;;; Initialization.
;;; The cold loader (Genesis) makes the data structure in *initial-symbols*.
;;; We grovel over it, making the specified packages and interning the
;;; symbols. For a description of the format of *initial-symbols* see
;;; the Genesis source.
(defvar *initial-symbols*)
(defun package-init ()
(let ((*in-package-init* t))
(dolist (spec *initial-symbols*)
(let* ((pkg (apply #'make-package (first spec)))
(internal (package-internal-symbols pkg))
(external (package-external-symbols pkg)))
;;
;; Put internal symbols in the internal hashtable and set package.
(dolist (symbol (second spec))
(add-symbol internal symbol)
(%set-symbol-package symbol pkg))
;;
;; External symbols same, only go in external table.
(dolist (symbol (third spec))
(add-symbol external symbol)
(%set-symbol-package symbol pkg))
;;
;; Don't set package for Imported symbols.
(dolist (symbol (fourth spec))
(add-symbol internal symbol))
(dolist (symbol (fifth spec))
(add-symbol external symbol))
;;
;; Put shadowing symbols in the shadowing symbols list.
(setf (package-%shadowing-symbols pkg) (sixth spec))))
(makunbound '*initial-symbols*) ; So it gets GC'ed.
;; Make some other packages that should be around in the cold load:
(make-package "COMMON-LISP-USER" :nicknames '("CL-USER" "USER"))
;; Now do the *deferred-use-packages*:
(dolist (args *deferred-use-packages*)
(apply #'use-package args))
(makunbound '*deferred-use-packages*)
(setq *lisp-package* (find-package "LISP"))
(setq *keyword-package* (find-package "KEYWORD"))
;; For the kernel core image wizards, set the package to *Lisp-Package*.
(setq *package* *lisp-package*)))